面向对象设计有几个原则(开闭、单一等),如果能够按照这些原则来开发软件,就能充分发挥面向对象的好处(低耦合,易扩展)。设计模式正式这些原则的体现,它是前人编码过程中的经验总结,针对某些问题而提出的解决方案。
最经典的设计模式出自《设计模式-可复用面向对象软件的基础》,针对不同具体问题,书中提出了 23 个设计模式,可大致分为三类:创建型模式、结构模式 和 行为模式。
本文介绍第一大类设计模式:创建型模式。
单例、工厂、抽象工厂、原型、建造者
开发中会用到很多对象、最佳的使用对象的方式,是遵循面向对象的一个原则“针对接口编程”,即在程序代码中出现的对象,都是接口类型,而不要出现具体的类,创建型模式大部分都是为了解决这个问题,即:对象创建交给专门的几个类去负责,创建完之后交给用户使用,用户得到的是一个接口类型,既不必知道这些类具体是什么(只要知道它们都是某个接口的实现类),也不必知道是如何创建它们的。
创建唯一的一个对象
设计:
classA{
privatestatic A single=null;
publicstatic A getSingleton(){
if(null==single){ single=new A(); }
return single; } }
客户端:
A a = A.getSingleton();
注意:如果是并发环境中、或者在好几个虚拟机之间创建唯一的对象,则要再用其他的方法,如并发控制、rmi 等。
最常见的一种模式,可在 Java 类库中大量见到,用户只需要调用工厂方法类的相应方法即可创建对象。
设计:
//Product 产品接口
interfaceProduct{}
//具体产品A
classProductAimplementsProduct{}
//Factory 工厂接口
interfaceFactory{
Product create(); }
classFactoryAimplementsFactory{
@Override public Product create(){
returnnewProductA(); } }
客户端:
Factory factory = new FactoryA(); Product product = factory.create();
说明:
要修改 Product 对象的创建方式,客户端代码并不受影响
要创建另一种 Product 对象(如 ProductB ),则创建一个 Facrory 的子类 FactoryB,然后用这个对象来创建 ProductB 。虽然也修改了代码,但是仅仅限于创建 Factory 对象的地方,其他的业务逻辑部分并没有修改(业务逻辑指用到了 Product 类型的地方)
工厂方法的一种改进,叫做静态工厂方法,如果要创建另一种 Product 对象,把该过程写在工厂类的静态方法里,这在 JDK 类库里大量使用
创建一组对象
上面的工厂方法其实并没有体现出好处,因为每个工厂只创建一种对象,但是如果需要创建一批对象,好处就体现出来了。抽象工厂方法适用于类似下列情形:比如要更换程序界面的显示风格,如窗口、按钮、滚动条等。如果不用抽象工厂,就得一个个对象都替换掉,这显然不简单。实际上高层业务逻辑也只是针对这些窗口或者按钮的接口进行编程,抽象工厂可以很容地把这些对象立刻换成另外一批。
设计:
//窗口对象接口
interfaceWindow{}
//某种风格A的窗口
classWindowAimplementsWindow{}
//按钮对象接口
interfaceButton{}
//某种风格A的按钮
classButtonAimplementsButton{}
//工厂接口
interfaceFactory{
//创建窗口 Window createWindow();
//创建按钮 Button createButton(); }
//生产某种风格A对象的工厂
classFactoryAimplementsFactory{
@Override public Window createWindow(){
returnnewWindowA(); }
@Override public Button createButton(){
returnnewButtonA(); } }
客户端:
Factory facory = new FactoryA(); Window window = factory.createWindow(); Button button = factory.createButton();
对象可以自身复制出新的对象
适用场景:创建的对象很多属性都差不多,只需要在其他对象上修改一小部分。很多编程语言支持这种方式创建对象,比如 Java 中每个对象都有 clone ( ) 方法,那么在这样的语言里,不用专门写这种设计模式、利用自带方法即可。
设计:
classA{
//复制自己的实例 A clone(){} }
客户端:
A a = new A(); A b = a.clone();
//下面b可修改一些属性,成为一个新的对象
对象的创建其实是一个非常复杂的过程,可能需要先创建一些零件,最后再把这些零件组装起来,建造者模式把组装这个行为分离出来,使得相同的零件,可以用不同的组装方法。
设计:
/*创建一个个零件类*/
//假设A、B是零件的接口
classBuilder{
A createA();
B createB(); }
//定义零件组装过程
classDirector{ Builder builder; Director(Builder builder){
this.builder = builder; }
voidassembly(){ A a = builder.createA(); B b = builder.createB();
//下面就是根据某种方法,将a和b组装起来 } }
客户端:
Builder builder = new Builder(); Director director = new Director(builder); director.assembly();
说明:
这里的 Builder 和 Director 完全可以定义成接口,利用前面讲的工厂方法等来提供,也就是说设计模式之间是可以组合使用的,最终可以使得客户端中不存在 new 这种显示创建对象的方法。
小结
1.创建型模式是设计模式的基础,只有把这个问题解决了,上层模块才能完全对接口进行编程,想要哪个对象,直接调用某个创建型模式对象的方法就可以了。细心看上面的例子可以发现:尽管业务对象可以通过工厂类对象等创建,没有显示出现 new 关键字,但是工厂类对象本身却出现了 new 关键字。
2.有两种方法可以彻底消灭 new
把创建这些工厂类的代码聚集到一起,使变化局限于局部;
Java 中有「反射」机制,可以把创建工厂类的方式通过配置文件来提供,这样就完全避免了修改代码,完全避免了代码中出现 new ,这是最佳的解决方案:即工厂对象由配置文件创建,业务对象由工厂对象创建。很多框架,例如 Spring ,就是采取这种方式来提供对象,它有个时髦的名字叫做「依赖注入」。